home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.002 / stk-3 / STk-3.1 / Tk / generic / tkFocus.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-31  |  23.8 KB  |  836 lines

  1. /* 
  2.  * tkFocus.c --
  3.  *
  4.  *    This file contains procedures that manage the input
  5.  *    focus for Tk.
  6.  *
  7.  * Copyright (c) 1990-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * SCCS: @(#) tkFocus.c 1.27 96/02/15 18:53:29
  14.  */
  15.  
  16. #include "tkInt.h"
  17. #include "tkPort.h"
  18.  
  19. /*
  20.  * For each top-level window that has ever received the focus, there
  21.  * is a record of the following type:
  22.  */
  23.  
  24. typedef struct TkFocusInfo {
  25.     TkWindow *topLevelPtr;    /* Information about top-level window. */
  26.     TkWindow *focusWinPtr;    /* The next time the focus comes to this
  27.                  * top-level, it will be given to this
  28.                  * window. */
  29.     struct TkFocusInfo *nextPtr;/* Next in list of all focus records for
  30.                  * a given application. */
  31. } FocusInfo;
  32.  
  33. static int focusDebug = 0;
  34.  
  35. /*
  36.  * The following magic value is stored in the "send_event" field of
  37.  * FocusIn and FocusOut events that are generated in this file.  This
  38.  * allows us to separate "real" events coming from the server from
  39.  * those that we generated.
  40.  */
  41.  
  42. #define GENERATED_EVENT_MAGIC ((Bool) 0x547321ac)
  43.  
  44. /*
  45.  * Forward declarations for procedures defined in this file:
  46.  */
  47.  
  48.  
  49. static void        ChangeXFocus _ANSI_ARGS_((TkWindow *topLevelPtr,
  50.                 int focus));
  51. static void        FocusMapProc _ANSI_ARGS_((ClientData clientData,
  52.                 XEvent *eventPtr));
  53. static void        GenerateFocusEvents _ANSI_ARGS_((TkWindow *sourcePtr,
  54.                 TkWindow *destPtr));
  55. static void        SetFocus _ANSI_ARGS_((TkWindow *winPtr, int force));
  56.  
  57. /*
  58.  *--------------------------------------------------------------
  59.  *
  60.  * Tk_FocusCmd --
  61.  *
  62.  *    This procedure is invoked to process the "focus" Tcl command.
  63.  *    See the user documentation for details on what it does.
  64.  *
  65.  * Results:
  66.  *    A standard Tcl result.
  67.  *
  68.  * Side effects:
  69.  *    See the user documentation.
  70.  *
  71.  *--------------------------------------------------------------
  72.  */
  73.  
  74. int
  75. Tk_FocusCmd(clientData, interp, argc, argv)
  76.     ClientData clientData;    /* Main window associated with
  77.                  * interpreter. */
  78.     Tcl_Interp *interp;        /* Current interpreter. */
  79.     int argc;            /* Number of arguments. */
  80.     char **argv;        /* Argument strings. */
  81. {
  82.     Tk_Window tkwin = (Tk_Window) clientData;
  83.     TkWindow *winPtr = (TkWindow *) clientData;
  84.     TkWindow *newPtr, *focusWinPtr, *topLevelPtr;
  85.     FocusInfo *focusPtr;
  86.     char c;
  87.     size_t length;
  88.  
  89.     /*
  90.      * If invoked with no arguments, just return the current focus window.
  91.      */
  92.  
  93.     if (argc == 1) {
  94.     focusWinPtr = TkGetFocus(winPtr);
  95.     if (focusWinPtr != NULL) {
  96. #ifdef STk_CODE
  97.         STk_sharp_dot_result(interp, focusWinPtr->pathName);
  98. #else
  99.         interp->result = focusWinPtr->pathName;
  100. #endif
  101.     }
  102. #ifdef STk_CODE
  103.     else 
  104.       interp->result = "#f";
  105. #endif
  106.     return TCL_OK;
  107.     }
  108.  
  109.     /*
  110.      * If invoked with a single argument beginning with "." then focus
  111.      * on that window.
  112.      */
  113.  
  114.     if (argc == 2) {
  115.     if (argv[1][0] == 0) {
  116.         return TCL_OK;
  117.     }
  118.     if (argv[1][0] == '.') {
  119.         newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[1], tkwin);
  120.         if (newPtr == NULL) {
  121.         return TCL_ERROR;
  122.         }
  123.         if (!(newPtr->flags & TK_ALREADY_DEAD)) {
  124.         SetFocus(newPtr, 0);
  125.         }
  126.         return TCL_OK;
  127.     }
  128.     }
  129.  
  130.     length = strlen(argv[1]);
  131.     c = argv[1][1];
  132.     if ((c == 'd') && (strncmp(argv[1], "-displayof", length) == 0)) {
  133.     if (argc != 3) {
  134.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  135. #ifdef STk_CODE
  136.             argv[0], " :displayof window\"", (char *) NULL);
  137. #else
  138.             argv[0], " -displayof window\"", (char *) NULL);
  139. #endif
  140.         return TCL_ERROR;
  141.     }
  142.     newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
  143.     if (newPtr == NULL) {
  144.         return TCL_ERROR;
  145.     }
  146.     newPtr = TkGetFocus(newPtr);
  147.     if (newPtr != NULL) {
  148.         interp->result = newPtr->pathName;
  149.     }
  150.     } else if ((c == 'f') && (strncmp(argv[1], "-force", length) == 0)) {
  151.     if (argc != 3) {
  152.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  153. #ifdef STk_CODE
  154.             argv[0], " :force window\"", (char *) NULL);
  155. #else
  156.             argv[0], " -force window\"", (char *) NULL);
  157. #endif
  158.         return TCL_ERROR;
  159.     }
  160.     if (argv[2][0] == 0) {
  161.         return TCL_OK;
  162.     }
  163.     newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
  164.     if (newPtr == NULL) {
  165.         return TCL_ERROR;
  166.     }
  167.     SetFocus(newPtr, 1);
  168.     } else if ((c == 'l') && (strncmp(argv[1], "-lastfor", length) == 0)) {
  169.     if (argc != 3) {
  170.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  171. #ifdef STk_CODE
  172.             argv[0], " :lastfor window\"", (char *) NULL);
  173. #else
  174.             argv[0], " -lastfor window\"", (char *) NULL);
  175. #endif
  176.         return TCL_ERROR;
  177.     }
  178.     newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
  179.     if (newPtr == NULL) {
  180.         return TCL_ERROR;
  181.     }
  182.     for (topLevelPtr = newPtr; topLevelPtr != NULL;
  183.         topLevelPtr = topLevelPtr->parentPtr)  {
  184.         if (topLevelPtr->flags & TK_TOP_LEVEL) {
  185.         for (focusPtr = newPtr->mainPtr->focusPtr; focusPtr != NULL;
  186.             focusPtr = focusPtr->nextPtr) {
  187.             if (focusPtr->topLevelPtr == topLevelPtr) {
  188. #ifdef STk_CODE
  189.             STk_sharp_dot_result(interp,
  190.                            focusPtr->focusWinPtr->pathName);
  191. #else
  192.             interp->result = focusPtr->focusWinPtr->pathName;
  193. #endif
  194.             return TCL_OK;
  195.             }
  196.         }
  197. #ifdef STk_CODE
  198.         STk_sharp_dot_result(interp, topLevelPtr->pathName);
  199. #else
  200.         interp->result = topLevelPtr->pathName;
  201. #endif
  202.         return TCL_OK;
  203.         }
  204.     }
  205.     } else {
  206.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  207. #ifdef STk_CODE
  208.         "\": must be :displayof, :force, or :lastfor", (char *) NULL);
  209. #else
  210.         "\": must be -displayof, -force, or -lastfor", (char *) NULL);
  211. #endif
  212.     return TCL_ERROR;
  213.     }
  214.     return TCL_OK;
  215. }
  216.  
  217. /*
  218.  *--------------------------------------------------------------
  219.  *
  220.  * TkFocusFilterEvent --
  221.  *
  222.  *    This procedure is invoked by Tk_HandleEvent when it encounters
  223.  *    a FocusIn, FocusOut, Enter, or Leave event.
  224.  *
  225.  * Results:
  226.  *    A return value of 1 means that Tk_HandleEvent should process
  227.  *    the event normally (i.e. event handlers should be invoked).
  228.  *    A return value of 0 means that this event should be ignored.
  229.  *
  230.  * Side effects:
  231.  *    Additional events may be generated, and the focus may switch.
  232.  *
  233.  *--------------------------------------------------------------
  234.  */
  235.  
  236. int
  237. TkFocusFilterEvent(winPtr, eventPtr)
  238.     TkWindow *winPtr;        /* Window that focus event is directed to. */
  239.     XEvent *eventPtr;        /* FocusIn or FocusOut event. */
  240. {
  241.     /*
  242.      * Design notes: the window manager and X server work together to
  243.      * transfer the focus among top-level windows.  This procedure takes
  244.      * care of transferring the focus from a top-level window to the
  245.      * actual window within that top-level that has the focus.  We
  246.      * do this by synthesizing X events to move the focus around.  None
  247.      * of the FocusIn and FocusOut events generated by X are ever used
  248.      * outside of this procedure;  only the synthesized events get through
  249.      * to the rest of the application.  At one point (e.g. Tk4.0b1) Tk
  250.      * used to call X to move the focus from a top-level to one of its
  251.      * descendants, then just pass through the events generated by X.
  252.      * This approach didn't work very well, for a variety of reasons.
  253.      * For example, if X generates the events they go at the back of
  254.      * the event queue, which could cause problems if other things
  255.      * have already happened, such as moving the focus to yet another
  256.      * window.
  257.      */
  258.  
  259.     FocusInfo *focusPtr;
  260.     TkDisplay *dispPtr = winPtr->dispPtr;
  261.     TkWindow *newFocusPtr;
  262.     int retValue, delta;
  263.  
  264.     /*
  265.      * If this was a generated event, just turn off the generated
  266.      * flag and pass the event through.
  267.      */
  268.  
  269.     if (eventPtr->xfocus.send_event == GENERATED_EVENT_MAGIC) {
  270.     eventPtr->xfocus.send_event = 0;
  271.     return 1;
  272.     }
  273.     /*
  274.      * This was not a generated event.  We'll return 1 (so that the
  275.      * event will be processed) if it's an Enter or Leave event, and
  276.      * 0 (so that the event won't be processed) if it's a FocusIn or
  277.      * FocusOut event.  Also, skip NotifyPointer, NotifyPointerRoot,
  278.      * and NotifyInferior focus events immediately; they're not
  279.      * useful and tend to cause confusion.
  280.      */
  281.  
  282.     if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
  283.     retValue = 0;
  284.     if ((eventPtr->xfocus.detail == NotifyPointer)
  285.         || (eventPtr->xfocus.detail == NotifyPointerRoot)
  286.         || (eventPtr->xfocus.detail == NotifyInferior)) {
  287.         return retValue;
  288.     }
  289.     } else {
  290.     retValue = 1;
  291.     if (eventPtr->xcrossing.detail == NotifyInferior) {
  292.         return retValue;
  293.     }
  294.     }
  295.  
  296.     /*
  297.      * If winPtr isn't a top-level window than just ignore the event.
  298.      */
  299.  
  300.     if (!(winPtr->flags & TK_TOP_LEVEL)) {
  301.     return retValue;
  302.     }
  303.  
  304.     /*
  305.      * If there is a grab in effect and this window is outside the
  306.      * grabbed tree, then ignore the event.
  307.      */
  308.  
  309.     if (TkGrabState(winPtr) == TK_GRAB_EXCLUDED)  {
  310.     return retValue;
  311.     
  312. }
  313.     /*
  314.      * Find the FocusInfo structure for the window, and make a new one
  315.      * if there isn't one already.
  316.      */
  317.  
  318.     for (focusPtr = winPtr->mainPtr->focusPtr; focusPtr != NULL;
  319.         focusPtr = focusPtr->nextPtr) {
  320.     if (focusPtr->topLevelPtr == winPtr) {
  321.         break;
  322.     }
  323.     }
  324.     if (focusPtr == NULL) {
  325.     focusPtr = (FocusInfo *) ckalloc(sizeof(FocusInfo));
  326.     focusPtr->topLevelPtr = focusPtr->focusWinPtr = winPtr;
  327.     focusPtr->nextPtr = winPtr->mainPtr->focusPtr;
  328.     winPtr->mainPtr->focusPtr = focusPtr;
  329.     }
  330.  
  331.     /*
  332.      * It is possible that there were outstanding FocusIn and FocusOut
  333.      * events on their way to us at the time the focus was changed
  334.      * internally with the "focus" command.  If so, these events could
  335.      * potentially cause us to lose the focus (switch it to the window
  336.      * of the last FocusIn event) even though the focus change occurred
  337.      * after those events.  The following code detects this and puts
  338.      * the focus back to the place where it was rightfully set.
  339.      */
  340.  
  341.     newFocusPtr = focusPtr->focusWinPtr;
  342.  
  343.     delta = eventPtr->xfocus.serial - winPtr->mainPtr->focusSerial;
  344.     if (focusDebug) {
  345.     printf("check event serial %d, delta %d\n",
  346.         (int) eventPtr->xfocus.serial, delta);
  347.     }
  348.     if ((delta < 0) && (winPtr->mainPtr->lastFocusPtr != NULL)) {
  349.     newFocusPtr = winPtr->mainPtr->lastFocusPtr;
  350.     if (focusDebug) {
  351.         printf("reverting to %s instead of %s\n", newFocusPtr->pathName,
  352.             focusPtr->focusWinPtr->pathName);
  353.     }
  354.     }
  355.  
  356.     if (eventPtr->type == FocusIn) {
  357.     GenerateFocusEvents(dispPtr->focusWinPtr, newFocusPtr);
  358.     dispPtr->focusWinPtr = newFocusPtr;
  359.     dispPtr->implicitWinPtr = NULL;
  360.     if (focusDebug) {
  361.         printf("Focussed on %s\n", newFocusPtr->pathName);
  362.     }
  363.     } else if (eventPtr->type == FocusOut) {
  364.     GenerateFocusEvents(dispPtr->focusWinPtr, (TkWindow *) NULL);
  365.     dispPtr->focusWinPtr = NULL;
  366.     dispPtr->implicitWinPtr = NULL;
  367.     if (focusDebug) {
  368.         printf("Unfocussed from %s, detail %d\n", winPtr->pathName,
  369.             eventPtr->xfocus.detail);
  370.     }
  371.     } else if (eventPtr->type == EnterNotify) {
  372.     /*
  373.      * If there is no window manager, or if the window manager isn't
  374.      * moving the focus around (e.g. the disgusting "NoTitleFocus"
  375.      * option has been selected in twm), then we won't get FocusIn
  376.      * or FocusOut events.  Instead, the "focus" field will be set
  377.      * in an Enter event to indicate that we've already got the focus
  378.      * when then mouse enters the window (even though we didn't get
  379.      * a FocusIn event).  Watch for this and grab the focus when it
  380.      * happens.
  381.      */
  382.  
  383.     if (eventPtr->xcrossing.focus && (dispPtr->focusWinPtr == NULL)) {
  384.         GenerateFocusEvents(dispPtr->focusWinPtr, newFocusPtr);
  385.         dispPtr->focusWinPtr = newFocusPtr;
  386.         dispPtr->implicitWinPtr = winPtr;
  387.         if (focusDebug) {
  388.         printf("Focussed implicitly on %s\n",
  389.             newFocusPtr->pathName);
  390.         }
  391.     }
  392.     } else if (eventPtr->type == LeaveNotify) {
  393.     /*
  394.      * If the pointer just left a window for which we automatically
  395.      * claimed the focus on enter, generate FocusOut events.  Note:
  396.      * dispPtr->implicitWinPtr may not be the same as
  397.      * dispPtr->focusWinPtr (e.g. because the "focus" command was
  398.      * used to redirect the focus after it arrived at
  399.      * dispPtr->implicitWinPtr)!!
  400.      */
  401.  
  402.     if (dispPtr->implicitWinPtr == winPtr) {
  403.         GenerateFocusEvents(dispPtr->focusWinPtr, (TkWindow *) NULL);
  404.         dispPtr->focusWinPtr = NULL;
  405.         dispPtr->implicitWinPtr = NULL;
  406.         if (focusDebug) {
  407.         printf("Defocussed implicitly\n");
  408.         }
  409.     }
  410.     }
  411.     return retValue;
  412. }
  413.  
  414. /*
  415.  *----------------------------------------------------------------------
  416.  *
  417.  * SetFocus --
  418.  *
  419.  *    This procedure is invoked to change the focus window for a
  420.  *    given display in a given application.
  421.  *
  422.  * Results:
  423.  *    None.
  424.  *
  425.  * Side effects:
  426.  *    Event handlers may be invoked to process the change of
  427.  *    focus.
  428.  *
  429.  *----------------------------------------------------------------------
  430.  */
  431.  
  432. static void
  433. SetFocus(winPtr, force)
  434.     TkWindow *winPtr;        /* Window that is to be the new focus for
  435.                  * its display and application. */
  436.     int force;            /* If non-zero, set the X focus to this
  437.                  * window even if the application doesn't
  438.                  * currently have the X focus. */
  439. {
  440.     TkDisplay *dispPtr = winPtr->dispPtr;
  441.     FocusInfo *focusPtr;
  442.     TkWindow *topLevelPtr, *topLevelPtr2;
  443.  
  444.     if (winPtr == dispPtr->focusWinPtr) {
  445.     return;
  446.     }
  447.  
  448.     /*
  449.      * Find the top-level window for winPtr, then find (or create)
  450.      * a record for the top-level.
  451.      */
  452.  
  453.     for (topLevelPtr = winPtr; ; topLevelPtr = topLevelPtr->parentPtr)  {
  454.     if (topLevelPtr == NULL) {
  455.         /*
  456.          * The window is being deleted.  No point in worrying about
  457.          * giving it the focus.
  458.          */
  459.  
  460.         return;
  461.     }
  462.     if (topLevelPtr->flags & TK_TOP_LEVEL) {
  463.         break;
  464.     }
  465.     }
  466.     for (focusPtr = winPtr->mainPtr->focusPtr; focusPtr != NULL;
  467.         focusPtr = focusPtr->nextPtr) {
  468.     if (focusPtr->topLevelPtr == topLevelPtr) {
  469.         break;
  470.     }
  471.     }
  472.     if (focusPtr == NULL) {
  473.     focusPtr = (FocusInfo *) ckalloc(sizeof(FocusInfo));
  474.     focusPtr->topLevelPtr = topLevelPtr;
  475.     focusPtr->nextPtr = winPtr->mainPtr->focusPtr;
  476.     winPtr->mainPtr->focusPtr = focusPtr;
  477.     }
  478.  
  479.     /*
  480.      * Reset the focus, but only if the application already has the
  481.      * input focus or "force" has been specified.
  482.      */
  483.  
  484.     focusPtr->focusWinPtr = winPtr;
  485.     Tk_MakeWindowExist((Tk_Window) winPtr);
  486.     if (force || ((dispPtr->focusWinPtr != NULL)
  487.         && (dispPtr->focusWinPtr->mainPtr == winPtr->mainPtr))) {
  488.     /*
  489.      * Reset the focus in X if it has changed top-levels and if the
  490.      * new top-level isn't override-redirect (the only reason to
  491.      * change the X focus is so that the window manager can redecorate
  492.      * the focus window, but if it's override-redirect then it won't
  493.      * be decorated anyway;  also, changing the focus to menus causes
  494.      * all sorts of problems with olvwm:  the focus gets lost if
  495.      * keyboard traversal is used to move among menus.
  496.      */
  497.  
  498.     if (dispPtr->focusWinPtr != NULL) {
  499.         for (topLevelPtr2 = dispPtr->focusWinPtr;
  500.             (topLevelPtr2 != NULL)
  501.             && !(topLevelPtr2->flags & TK_TOP_LEVEL);
  502.             topLevelPtr2 = topLevelPtr2->parentPtr)  {
  503.         /* Empty loop body. */
  504.         }
  505.     } else {
  506.         topLevelPtr2 = NULL;
  507.     }
  508.     if ((topLevelPtr2 != topLevelPtr)
  509.         && !(topLevelPtr->atts.override_redirect)) {
  510.         if (dispPtr->focusOnMapPtr != NULL) {
  511.         Tk_DeleteEventHandler((Tk_Window) dispPtr->focusOnMapPtr,
  512.             StructureNotifyMask, FocusMapProc,
  513.             (ClientData) dispPtr->focusOnMapPtr);
  514.         dispPtr->focusOnMapPtr = NULL;
  515.         }
  516.         if (topLevelPtr->flags & TK_MAPPED) {
  517.         ChangeXFocus(topLevelPtr, force);
  518.         } else {
  519.         /*
  520.          * The window isn't mapped, so we can't give it the focus
  521.          * right now.  Create an event handler that will give it
  522.          * the focus as soon as it is mapped.
  523.          */
  524.  
  525.         Tk_CreateEventHandler((Tk_Window) topLevelPtr,
  526.             StructureNotifyMask, FocusMapProc,
  527.             (ClientData) topLevelPtr);
  528.         dispPtr->focusOnMapPtr = topLevelPtr;
  529.         dispPtr->forceFocus = force;
  530.         }
  531.     }
  532.     GenerateFocusEvents(dispPtr->focusWinPtr, winPtr);
  533.     dispPtr->focusWinPtr = winPtr;
  534.     }
  535.  
  536.     /*
  537.      * Remember the current serial number for the X server and issue
  538.      * a dummy server request.  This marks the position at which we
  539.      * changed the focus, so we can distinguish FocusIn and FocusOut
  540.      * events on either side of the mark.
  541.      */
  542.  
  543.     winPtr->mainPtr->lastFocusPtr = winPtr;
  544.     winPtr->mainPtr->focusSerial = NextRequest(winPtr->display);
  545.     XNoOp(winPtr->display);
  546.     if (focusDebug) {
  547.     printf("focus marking for %s at %d\n", winPtr->pathName,
  548.         (int) winPtr->mainPtr->focusSerial);
  549.     }
  550. }
  551.  
  552. /*
  553.  *----------------------------------------------------------------------
  554.  *
  555.  * TkGetFocus --
  556.  *
  557.  *    Given a window, this procedure returns the current focus
  558.  *    window for its application and display.
  559.  *
  560.  * Results:
  561.  *    The return value is a pointer to the window that currently
  562.  *    has the input focus for the specified application and
  563.  *    display, or NULL if none.
  564.  *
  565.  * Side effects:
  566.  *    None.
  567.  *
  568.  *----------------------------------------------------------------------
  569.  */
  570.  
  571. TkWindow *
  572. TkGetFocus(winPtr)
  573.     TkWindow *winPtr;        /* Window that selects an application
  574.                  * and a display. */
  575. {
  576.     TkWindow *focusWinPtr;
  577.  
  578.     focusWinPtr = winPtr->dispPtr->focusWinPtr;
  579.     if ((focusWinPtr != NULL) && (focusWinPtr->mainPtr == winPtr->mainPtr)) {
  580.     return focusWinPtr;
  581.     }
  582.     return (TkWindow *) NULL;
  583. }
  584.  
  585. /*
  586.  *----------------------------------------------------------------------
  587.  *
  588.  * TkFocusDeadWindow --
  589.  *
  590.  *    This procedure is invoked when it is determined that
  591.  *    a window is dead.  It cleans up focus-related information
  592.  *    about the window.
  593.  *
  594.  * Results:
  595.  *    None.
  596.  *
  597.  * Side effects:
  598.  *    Various things get cleaned up and recycled.
  599.  *
  600.  *----------------------------------------------------------------------
  601.  */
  602.  
  603. void
  604. TkFocusDeadWindow(winPtr)
  605.     register TkWindow *winPtr;        /* Information about the window
  606.                      * that is being deleted. */
  607. {
  608.     FocusInfo *focusPtr, *prevPtr;
  609.     TkDisplay *dispPtr = winPtr->dispPtr;
  610.  
  611.     /*
  612.      * Search for focus records that refer to this window either as
  613.      * the top-level window or the current focus window.
  614.      */
  615.  
  616.     for (prevPtr = NULL, focusPtr = winPtr->mainPtr->focusPtr;
  617.         focusPtr != NULL;
  618.         prevPtr = focusPtr, focusPtr = focusPtr->nextPtr) {
  619.     if (winPtr == focusPtr->topLevelPtr) {
  620.         /*
  621.          * The top-level window is the one being deleted: free
  622.          * the focus record and release the focus back to PointerRoot
  623.          * if we acquired it implicitly.
  624.          */
  625.  
  626.         if (dispPtr->implicitWinPtr == winPtr) {
  627.         if (focusDebug) {
  628.             printf("releasing focus to root after %s died\n",
  629.                 focusPtr->topLevelPtr->pathName);
  630.         }
  631.         dispPtr->implicitWinPtr = NULL;
  632.         dispPtr->focusWinPtr = NULL;
  633.         }
  634.         if (dispPtr->focusWinPtr == focusPtr->focusWinPtr) {
  635.         dispPtr->focusWinPtr = NULL;
  636.         }
  637.         if (dispPtr->focusOnMapPtr == focusPtr->topLevelPtr) {
  638.         dispPtr->focusOnMapPtr = NULL;
  639.         }
  640.         if (prevPtr == NULL) {
  641.         winPtr->mainPtr->focusPtr = focusPtr->nextPtr;
  642.         } else {
  643.         prevPtr->nextPtr = focusPtr->nextPtr;
  644.         }
  645.         ckfree((char *) focusPtr);
  646.         break;
  647.     } else if (winPtr == focusPtr->focusWinPtr) {
  648.         /*
  649.          * The deleted window had the focus for its top-level:
  650.          * move the focus to the top-level itself.
  651.          */
  652.  
  653.         focusPtr->focusWinPtr = focusPtr->topLevelPtr;
  654.         if ((dispPtr->focusWinPtr == winPtr)
  655.             && !(focusPtr->topLevelPtr->flags & TK_ALREADY_DEAD)) {
  656.         if (focusDebug) {
  657.             printf("forwarding focus to %s after %s died\n",
  658.                 focusPtr->topLevelPtr->pathName, winPtr->pathName);
  659.         }
  660.         GenerateFocusEvents(dispPtr->focusWinPtr,
  661.             focusPtr->topLevelPtr);
  662.         dispPtr->focusWinPtr = focusPtr->topLevelPtr;
  663.         }
  664.         break;
  665.     }
  666.     }
  667.  
  668.     if (winPtr->mainPtr->lastFocusPtr == winPtr) {
  669.     winPtr->mainPtr->lastFocusPtr = NULL;
  670.     }
  671. }
  672.  
  673. /*
  674.  *----------------------------------------------------------------------
  675.  *
  676.  * GenerateFocusEvents --
  677.  *
  678.  *    This procedure is called to create FocusIn and FocusOut events to
  679.  *    move the input focus from one window to another.
  680.  *
  681.  * Results:
  682.  *    None.
  683.  *
  684.  * Side effects:
  685.  *    FocusIn and FocusOut events are generated.
  686.  *
  687.  *----------------------------------------------------------------------
  688.  */
  689.  
  690. static void
  691. GenerateFocusEvents(sourcePtr, destPtr)
  692.     TkWindow *sourcePtr;    /* Window that used to have the focus (may
  693.                  * be NULL). */
  694.     TkWindow *destPtr;        /* New window to have the focus (may be
  695.                  * NULL). */
  696.  
  697. {
  698.     XEvent event;
  699.     TkWindow *winPtr;
  700.  
  701.     winPtr = sourcePtr;
  702.     if (winPtr == NULL) {
  703.     winPtr = destPtr;
  704.     if (winPtr == NULL) {
  705.         return;
  706.     }
  707.     }
  708.  
  709.     event.xfocus.serial = LastKnownRequestProcessed(winPtr->display);
  710.     event.xfocus.send_event = GENERATED_EVENT_MAGIC;
  711.     event.xfocus.display = winPtr->display;
  712.     event.xfocus.mode = NotifyNormal;
  713.     TkInOutEvents(&event, sourcePtr, destPtr, FocusOut, FocusIn,
  714.         TCL_QUEUE_MARK);
  715. }
  716.  
  717. /*
  718.  *----------------------------------------------------------------------
  719.  *
  720.  * ChangeXFocus --
  721.  *
  722.  *    This procedure is invoked to move the official X focus from
  723.  *    one top-level to another.  We do this when the application
  724.  *    changes the focus window from one top-level to another, in
  725.  *    order to notify the window manager so that it can highlight
  726.  *    the new focus top-level.
  727.  *
  728.  * Results:
  729.  *    None.
  730.  *
  731.  * Side effects:
  732.  *    The official X focus window changes;  the application's focus
  733.  *    window isn't changed by this procedure.
  734.  *
  735.  *----------------------------------------------------------------------
  736.  */
  737.  
  738. static void
  739. ChangeXFocus(topLevelPtr, force)
  740.     TkWindow *topLevelPtr;    /* Top-level window that is to receive
  741.                  * the X focus. */
  742.     int force;            /* Non-zero means claim the focus even
  743.                  * if it didn't originally belong to
  744.                  * topLevelPtr's application. */
  745. {
  746.     TkDisplay *dispPtr = topLevelPtr->dispPtr;
  747.     TkWindow *winPtr;
  748.     Window focusWindow;
  749.     int dummy;
  750.     Tk_ErrorHandler errHandler;
  751.  
  752.     /*
  753.      * If the focus was received implicitly, then there's no advantage
  754.      * in setting an explicit focus;  just return.
  755.      */
  756.  
  757.     if (dispPtr->implicitWinPtr != NULL) {
  758.     return;
  759.     }
  760.  
  761.     /*
  762.      * Check to make sure that the focus is still in one of the
  763.      * windows of this application.  Furthermore, grab the server
  764.      * to make sure that the focus doesn't change in the middle
  765.      * of this operation.
  766.      */
  767.  
  768.     if (!focusDebug) {
  769.     XGrabServer(dispPtr->display);
  770.     }
  771.     if (!force) {
  772.     XGetInputFocus(dispPtr->display, &focusWindow, &dummy);
  773.     winPtr = (TkWindow *) Tk_IdToWindow(dispPtr->display, focusWindow);
  774.     if ((winPtr == NULL) || (winPtr->mainPtr != topLevelPtr->mainPtr)) {
  775.         goto done;
  776.     }
  777.     }
  778.  
  779.     /*
  780.      * Tell X to change the focus.  Ignore errors that occur when changing
  781.      * the focus:  it is still possible that the window we're focussing
  782.      * to could have gotten unmapped, which will generate an error.
  783.      */
  784.  
  785.     errHandler = Tk_CreateErrorHandler(dispPtr->display, -1, -1, -1,
  786.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  787.     XSetInputFocus(dispPtr->display, topLevelPtr->window, RevertToParent,
  788.         CurrentTime);
  789.     Tk_DeleteErrorHandler(errHandler);
  790.     if (focusDebug) {
  791.     printf("Set X focus to %s\n", topLevelPtr->pathName);
  792.     }
  793.  
  794.     done:
  795.     if (!focusDebug) {
  796.     XUngrabServer(dispPtr->display);
  797.     }
  798. }
  799.  
  800. /*
  801.  *----------------------------------------------------------------------
  802.  *
  803.  * FocusMapProc --
  804.  *
  805.  *    This procedure is called as an event handler for StructureNotify
  806.  *    events, if a window receives the focus at a time when its
  807.  *    toplevel isn't mapped.  The procedure is needed because X
  808.  *    won't allow the focus to be set to an unmapped window;  we
  809.  *    detect when the toplevel is mapped and set the focus to it then.
  810.  *
  811.  * Results:
  812.  *    None.
  813.  *
  814.  * Side effects:
  815.  *    If this is a map event, the focus gets set to the toplevel
  816.  *    given by clientData.
  817.  *
  818.  *----------------------------------------------------------------------
  819.  */
  820.  
  821. static void
  822. FocusMapProc(clientData, eventPtr)
  823.     ClientData clientData;    /* Toplevel window. */
  824.     XEvent *eventPtr;        /* Information about event. */
  825. {
  826.     TkWindow *winPtr = (TkWindow *) clientData;
  827.     TkDisplay *dispPtr = winPtr->dispPtr;
  828.  
  829.     if (eventPtr->type == MapNotify) {
  830.     ChangeXFocus(winPtr, dispPtr->forceFocus);
  831.     Tk_DeleteEventHandler((Tk_Window) winPtr, StructureNotifyMask,
  832.         FocusMapProc, clientData);
  833.     dispPtr->focusOnMapPtr = NULL;
  834.     }
  835. }
  836.